package bold.module.google;

import com.google.common.cache.*;
import com.google.common.util.concurrent.RateLimiter;
import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author Khai Loon
 * @date 2018/7/4
 */
public class Guava {


    @Test
    public void cache() {
        Cache<String, Integer> cc = CacheBuilder
                .newBuilder()
                .maximumSize(2)
                .expireAfterWrite(3, TimeUnit.SECONDS)
                .build();

        cc.put("key1", 1);
        cc.put("key2", 1);
        cc.put("key3", 1);
        System.out.println(cc.getIfPresent("key1"));
    }


    @Test
    public void rate1() throws InterruptedException {
        RateLimiter limiter = RateLimiter.create(2);//令牌桶

        CountDownLatch count = new CountDownLatch(2);
        for (int i = 1; i <= 2; i++) {

            new Thread(() -> {
                try {
                    count.await();

                    for (int n = 1; n <= 100; n++) {
                        limiter.acquire();
                        System.out.println(Thread.currentThread().getName() + " "
                                + System.currentTimeMillis() / 1000);
                    }


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }).start();
            count.countDown();
        }

        Thread.currentThread().join();
    }

    /**
     * 令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌，单独开辟的线程 有待源码分析？
     * 当桶满时，新添加的令牌被丢弃或拒绝。
     */
    @Test
    public void rate2() {
        RateLimiter limiter = RateLimiter.create(1);

        limiter.acquire();
        System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() / 1000);
//        Thread.sleep(3000L);
        //    Thread.sleep(11000L);

        limiter.acquire(1);
        System.out.println(Thread.currentThread().getName() + " " + System.currentTimeMillis() / 1000);

        limiter.acquire();
        System.out.println(Thread.currentThread().getName() + " "
                + System.currentTimeMillis() / 1000);

    }

    @Test
    public void rate3() throws InterruptedException {

    }

    /**
     * https://juejin.cn/post/7030979943068598303#heading-10
     * expireAfterWrite  到期移除
     * expireAfterAccess 读写操作都会重置访问时间
     * refreshAfterWrite 异步刷新,返回旧值
     * <p>
     * 探究点
     * 当缓存到达最大数量时回收最少使用的数据-》限制最大内存，避免内存被占满-》高级特性，赞👍
     * 基于时间的实体有效期，依据最后访问或写时间-》基本特性，但很细腻
     * 缓存的keys自动用，弱引用封装-》利于GC回收-》赞👍
     * 回收或被移除实体通知-》赞👍
     * 缓存的访问统计-》赞👍
     * <p>
     * 1、为什么要用 很多特性
     * 2、数据结构  hash表
     * 3、实现原理  构造器  数据过期重载 缓存回收机制
     * 4、注意细节
     */
    @Test
    public void testCache() throws InterruptedException {
        LoadingCache<Integer, String> cache
                //CacheBuilder的构造函数是私有的，只能通过其静态方法newBuilder()来获得CacheBuilder的实例
                = CacheBuilder.newBuilder()
                //设置并发级别为8，并发级别是指可以同时写缓存的线程数
                .concurrencyLevel(8)
                //设置写缓存后3秒钟过期
                .expireAfterWrite(3, TimeUnit.SECONDS)
                //设置缓存容器的初始容量为10
                .initialCapacity(10)
                //设置缓存最大容量为100，超过100之后就会按照LRU最近虽少使用算法来移除缓存项
                .maximumSize(100)
                //设置要统计缓存的命中率
                .recordStats()
                //设置缓存的移除通知
                .removalListener(new RemovalListener<Object, Object>() {
                    @Override
                    public void onRemoval(RemovalNotification<Object, Object> removal) {
                        System.out.println("过期的key：" + removal.getKey() + "  过期的value：" + removal.getValue());
                    }
                })
                //build方法中可以指定CacheLoader，在缓存不存在时通过CacheLoader的实现自动加载缓存
                .build(
                        new CacheLoader<Integer, String>() {
                            @Override
                            public String load(Integer key) {
                                return "新的字符串" + key;
                            }
                        }
                );

        cache.put(1, "字符串1");
        cache.put(2, "字符串2");
        cache.getIfPresent(3);

        TimeUnit.SECONDS.sleep(4);
        cache.getIfPresent(3);


        System.out.println("cache stats:");
        //最后打印缓存的命中率等 情况
        System.out.println(cache.stats().toString());
    }

    @Test
    public void testCache1()  {
        LoadingCache<String, String> cache=
                CacheBuilder.newBuilder()
                .concurrencyLevel(8)
                .expireAfterWrite(3, TimeUnit.SECONDS)
                .recordStats()
                .initialCapacity(1)
                .removalListener(new RemovalListener<Object, Object>() {
                    @Override
                    public void onRemoval(RemovalNotification<Object, Object> removal) {
                        System.out.println("过期的key：" + removal.getKey() + "  过期的value：" + removal.getValue());
                    }
                })
                .build(
                        new CacheLoader<String, String>() {
                            @Override
                            public String load(String key) {
                                int i = 1 / 0;
                                return "新的字符串"+System.currentTimeMillis();
                            }
                        }
                );

        try {
            System.out.println(cache.get("1"));
        } catch (ExecutionException e) {
            //加载缓存返回 null 报错逻辑
            e.printStackTrace();
        }

        try {
            System.out.println(cache.get("1"));
            System.out.println(cache.get("1"));
//        TimeUnit.SECONDS.sleep(4);
            System.out.println(cache.get("1"));
            System.out.println(cache.stats().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
